#pragma once

#include <unistd.h>
#include <pthread.h>
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
#include <fstream>
#include <sstream>
#include "Socket.hpp"

const std::string sep = "\r\n";
const std::string wwwroot = "./wwwroot";

struct threadInfo
{
    int _fd;
};

class HttpRequest
{
public:
    void Deserialize(std::string request)
    {
        while(true)
        {
            size_t pos = request.find(sep, 0);
            if(pos == std::string::npos) { return; }
            std::string tmpStr = request.substr(0, pos);
            if(tmpStr.empty()) { break; }
            _http_lines.push_back(std::move(tmpStr));
            request.erase(0, pos + 2);
        }
        _http_lines.push_back("");
        request.erase(0, 2);
        _content = request;
    }

    void Parse()
    {
        std::stringstream requestLine(_http_lines[0]);
        requestLine >> _method >> _url >> _version;    
    }

    void ForDebug()
    {
        std::cout << _method << " ++ " << _url << " ++ " << _version << std::endl;
        for(int i = 0; i < _http_lines.size(); ++i)
        {
            std::cout << _http_lines[i] << std::endl;
            std::cout << "----------------------" << std::endl;
        }
        std::cout << _content << std::endl;
    }

public:
    std::vector<std::string> _http_lines;
    std::string _method;
    std::string _url;
    std::string _version;
    std::string _content;
};

class HttpResponse
{
public:
    HttpResponse(int fd) : _fd(fd)
    { }

    void Response(const HttpRequest& req)
    {
        std::string url = req._url;
        std::string method = req._method;
        //构建http响应
        std::string path;
        if(url == "/") { path = wwwroot + "/index.html"; }
        else { path = wwwroot + url; }
        std::string suffix;
        GetSuffix(path, &suffix);
        // std::cout << suffix << std::endl;
        std::string resp_line;
        std::string content = HtmlRead(path);
        bool ok = true;
        if(content.empty()) {
            ok = false;
            std::string err_html = wwwroot;
            err_html += "/err.html";
            content = HtmlRead(err_html);
        }
        if(ok) { resp_line = "HTTP/1.0 200 OK"; }
        else { resp_line = "HTTP/1.0 404 Not Found"; }

        std::cout << "html read done..." << std::endl;
        // std::cout << content << std::endl;
        // std::string resp_line = "HTTP/1.0 302 Found";
        std::string resp_header = "Content-Length: ";
        std::string resp_blank = "\r\n";
        resp_header += std::to_string(content.size());
        // resp_header += "\r\n";
        // resp_header += "Location: https://www.baidu.com";
        resp_header += "\r\n";
        resp_header += "Set-Cookie: name=张三";
        resp_header += "\r\n";
        resp_header += "Set-Cookie: passwd=123456";
        resp_header += "\r\n";
        resp_header += "Content-Type: ";
        resp_header += suffix;
        // std::cout << suffix << std::endl;
        std::string resp = resp_line;
        resp += "\r\n";
        resp += resp_header;
        resp += "\r\n";
        resp += resp_blank;
        resp += content;
        // std::cout << resp << std::endl;
        send(_fd, resp.c_str(), resp.size(), 0);
    }
private:
    bool GetSuffix(std::string& path, std::string* out)
    {
        size_t pos = path.rfind(".");
        if(pos == std::string::npos) { return false; }
        std::string suffix = path.substr(pos + 1);
        if(suffix == "html") {
            *out = "text/html";
        }
        else if(suffix == "jpg") {
            *out = "application/x-jpg";
        }
        else if(suffix == "png") {
            *out = "image/png";
        }
        else if(suffix == "mp4") {
            *out = "video/mpeg4";
        }
        else {
            *out = "text/plain";
        }
        return true;
    }

    std::string HtmlRead(const std::string& path)
    {
        //bug ?? 无法对二进制文件进行读取
        // std::ifstream in(path);
        // if(!in.is_open()) { *out = "404"; return; }
        // std::string line;
        // while(std::getline(in, line)) {
        //     *out += line;
        // }
        // in.close();
        std::ifstream in(path, std::ios::binary);
        if(!in.is_open()) return "";

        in.seekg(0, std::ios_base::end);
        auto len = in.tellg();
        in.seekg(0, std::ios_base::beg);

        std::string content;
        content.resize(len);

        in.read((char*)content.c_str(), content.size());

        in.close();

        return content;
    }

public:
    int _fd;
};

class httpServer
{
public:
    void init(int port)
    {
        _listen_sock.Socket();
        _listen_sock.Bind(port);
        _listen_sock.Listen();
        std::cout << "init done..." << std::endl;
    }

    void start()
    {
        _is_runnning = true;
        while(_is_runnning)
        {
            std::string clientIp;
            uint16_t clientPort = 0;
            int fd = _listen_sock.Accept(&clientIp, &clientPort);
            threadInfo* Tinfo = new threadInfo();
            Tinfo->_fd = fd;
            pthread_t thread;
            pthread_create(&thread, nullptr, routine, Tinfo);
        }
        _is_runnning = false;
    }

    // void doTask(const HttpRequest& req, HttpResponse& res)
    // {
    //     std::string method = req._method;
    //     std::string url = req._url;
    //     if(url == "/") {  }
    // }

private:
    static void handler(int fd)
    {
        char buffer[10240]; buffer[0] = 0;
        ssize_t n = recv(fd, buffer, sizeof(buffer), 0);
        buffer[n] = 0;

        HttpRequest req;
        req.Deserialize(buffer);
        req.Parse();
        req.ForDebug();
        std::cout << std::endl;

        HttpResponse resp(fd);
        resp.Response(req);

        close(fd);
    }

    static void* routine(void* args)
    {
        pthread_detach(pthread_self());
        threadInfo* Tinfo = static_cast<threadInfo*>(args);
        int fd = Tinfo->_fd;
        handler(fd);
    }

private:
    Sock _listen_sock;
    bool _is_runnning = false;
};
